/**
 * *由一个组件，向上找到最近的指定组件；
 * *由一个组件，向上找到所有的指定组件；
 * *由一个组件，向下找到最近的指定组件；
 * *由一个组件，向下找到所有指定的组件；
 * *由一个组件，找到指定组件的兄弟组件。
 */

/**
 * @description 由一个组件，向上找到最近的指定组件；
 * @author fanzhongxu
 * @param {*} context 当前组件实例，通常传this即可
 * @param {*} componentName 要查找的组件名称
 * @returns 返回组件实例
 */
export function findComponentUpward(context, componentName) {
  let parent = context.$parent
  let name = parent.$options.name

  while (parent && (!name || [componentName].indexOf(name) < 0)) {
    parent = parent.$parent
    if (parent) name = parent.$options.name
  }

  return parent
}

/**
 * @description 由一个组件，向上找到所有的指定组件(递归)
 * @author fanzhongxu
 * @param {*} context 当前组件实例
 * @param {*} componentName 要查找的组件名称
 * @returns 返回数组，数组包含找到的组件实例
 */
export function findComponentsUpward(context, componentName) {
  const parents = []
  const parent = context.$parent

  if (parent) {
    if (parent.$options.name === componentName) parents.push(parent)
    return parents.concat(findComponentsUpward(parent, componentName))
  } else {
    return []
  }
}

/**
 * @description 由一个组件，向下找到最近的指定组件(递归)
 * @author fanzhongxu
 * @param {*} context 当前组件实例
 * @param {*} componentName 要查找的组件名称
 * @returns 返回组件实例
 */
export function findComponentDownward(context, componentName) {
  const children = context.$children
  let target

  if (children.length > 0) {
    for (const child of children) {
      if (child.$options.name === componentName) {
        target = child
        break
      } else {
        target = findComponentDownward(child, componentName)
        if (target) break
      }
    }
  }

  return target
}

/**
 * @description 由一个组件，向下找到所有指定的组件
 * @author fanzhongxu
 * @param {*} context 当前组件实例
 * @param {*} componentName 要查找的组件名称
 * @returns 返回数组，数组元素为目标组件实例
 */
export function findComponentsDownward(context, componentName) {
  return context.$children.reduce((components, child) => {
    if (child.$options.name === componentName) components.push(child)
    return components.concat(findComponentsDownward(child, componentName))
  }, [])
}

/**
 * @description 由一个组件，找到指定组件的兄弟组件
 * @author fanzhongxu
 * @param {*} context 当前组件实例
 * @param {*} componentName 要查找的组件名称
 * @param {boolean} [includeMe=false] 查找结果数组是否包含context
 * @returns 返回数组，数组元素为目标组件实例
 */
export function findBrotherComponents(
  context,
  componentName,
  includeMe = false
) {
  return context.$parent.$children.filter(
    com => com.$options.name === componentName && (includeMe || com !== context)
  )
}

/**
 * @description 深拷贝
 * @author fanzhongxu
 * @param {Object | Array} data 待拷贝的数据
 * @returns 拷贝后的数据
 */
export function deepClone(data) {
  const t = typeOf(data)
  let o

  if (t === 'array') {
    o = []
  } else if (t === 'object') {
    o = {}
  } else {
    return data
  }

  if (t === 'array') {
    for (let i = 0; i < data.length; i++) {
      o.push(deepClone(data[i]))
    }
  } else if (t === 'object') {
    for (const i in data) {
      o[i] = deepClone(data[i])
    }
  }
  return o
}

function typeOf(obj) {
  const toString = Object.prototype.toString
  const map = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object',
  }
  return map[toString.call(obj)]
}
